﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Input;
using System.Windows.Controls;
using MJB.UI.ContentEditor.UserControls;

namespace MJB.UI.ContentEditor.Commands
{
    /// <summary>
    /// This class holds the command for auto complete suggestions.
    /// When the user is typing in the section text box and types a &lt;
    /// key, a list box pops up over the text box and presents the user with 
    /// a list of XML options.  Each of these options is represented as a command
    /// in this class and its event handler is defined here as well.
    /// </summary>
    public class SuggestionCommands : DependencyObject
    {
        #region Commands

        /// <summary>
        /// This command inserts a new paragraph element into a designated text box.
        /// </summary>
        public static readonly RoutedUICommand NewParagraphCommand = new RoutedUICommand("NewParagraph",
            "NewParagraph",
            typeof(SuggestionCommands),
            new InputGestureCollection(new KeyGesture[] {
                new KeyGesture(Key.Enter, ModifierKeys.Control | ModifierKeys.Shift)
            }));

        /// <summary>
        /// This command inserts a Newline element into a designated text box.
        /// </summary>
        public static readonly RoutedUICommand NewlineCommand = new RoutedUICommand("Newline",
            "Newline",
            typeof(SuggestionCommands),
            new InputGestureCollection(new KeyGesture[] {
                new KeyGesture(Key.Enter, ModifierKeys.Shift)
            }));

        /// <summary>
        /// This command inserts a Bold element into a designated text box.
        /// </summary>
        public static readonly RoutedUICommand BoldCommand = new RoutedUICommand("Bold",
            "Bold",
            typeof(SuggestionCommands),
            new InputGestureCollection(new KeyGesture[] {
                new KeyGesture(Key.B, ModifierKeys.Control)
            }));

        /// <summary>
        /// This command inserts a Bold element into a designated text box.
        /// </summary>
        public static readonly RoutedUICommand CodeCommand = new RoutedUICommand("Code",
            "Code",
            typeof(SuggestionCommands),
            new InputGestureCollection(new KeyGesture[] {
                new KeyGesture(Key.D, ModifierKeys.Control)
            }));

        /// <summary>
        /// This command inserts a ContentLink element into a designated text box.
        /// </summary>
        public static readonly RoutedUICommand ContentLinkCommand = new RoutedUICommand("ContentLink",
            "ContentLink",
            typeof(SuggestionCommands),
            new InputGestureCollection(new KeyGesture[] {
                new KeyGesture(Key.L, ModifierKeys.Control)
            }));

        /// <summary>
        /// This command inserts a ExternalLink element into a designated text box.
        /// </summary>
        public static readonly RoutedUICommand ExternalLinkCommand = new RoutedUICommand("ExternalLink",
            "ExternalLink",
            typeof(SuggestionCommands),
            new InputGestureCollection(new KeyGesture[] {
                new KeyGesture(Key.E, ModifierKeys.Control)
            }));

        /// <summary>
        /// This command inserts a List into a designated text box.
        /// </summary>
        public static readonly RoutedUICommand ListCommand = new RoutedUICommand("List",
            "List",
            typeof(SuggestionCommands),
            new InputGestureCollection(new KeyGesture[] {
                new KeyGesture(Key.L, ModifierKeys.Control | ModifierKeys.Shift)
            }));

        /// <summary>
        /// This command inserts a ListElement into a designated text box.
        /// </summary>
        public static readonly RoutedUICommand ListElementCommand = new RoutedUICommand("ListElement",
            "ListElement",
            typeof(SuggestionCommands),
            new InputGestureCollection(new KeyGesture[] {
                new KeyGesture(Key.E, ModifierKeys.Control | ModifierKeys.Shift)
            }));

        /// <summary>
        /// This command inserts a Table into a designated text box.
        /// </summary>
        public static readonly RoutedUICommand TableCommand = new RoutedUICommand("Table",
            "Table",
            typeof(SuggestionCommands),
            new InputGestureCollection(new KeyGesture[] {
                new KeyGesture(Key.T, ModifierKeys.Control | ModifierKeys.Shift)
            }));

        /// <summary>
        /// This command inserts a TableRow into a designated text box.
        /// </summary>
        public static readonly RoutedUICommand TableRowCommand = new RoutedUICommand("TableRow",
            "TableRow",
            typeof(SuggestionCommands),
            new InputGestureCollection(new KeyGesture[] {
                new KeyGesture(Key.R, ModifierKeys.Control | ModifierKeys.Shift)
            }));

        /// <summary>
        /// This command inserts a TableCell into a designated text box.
        /// </summary>
        public static readonly RoutedUICommand TableCellCommand = new RoutedUICommand("TableCell",
            "TableCell",
            typeof(SuggestionCommands),
            new InputGestureCollection(new KeyGesture[] {
                new KeyGesture(Key.C, ModifierKeys.Control | ModifierKeys.Shift)
            }));

        /// <summary>
        /// This command inserts an Image into a designated text box.
        /// </summary>
        public static readonly RoutedUICommand ImageCommand = new RoutedUICommand("Image",
            "Image",
            typeof(SuggestionCommands),
            new InputGestureCollection(new KeyGesture[] {
                new KeyGesture(Key.I, ModifierKeys.Control)
            }));

        #endregion
        #region Dependency Properties

        /// <summary>
        /// </summary>
        public static readonly DependencyProperty AllCommandsProperty = DependencyProperty.Register("AllCommands",
            typeof(List<RoutedUICommand>),
            typeof(SuggestionCommands));

        #endregion
        #region Constructor

        /// <summary>
        /// Creates the suggestion commands object.  Creates the list of commands that 
        /// are used in this command list.
        /// </summary>
        public SuggestionCommands()
        {
            this.AllCommands = new List<RoutedUICommand>();
            this.AllCommands.Add(SuggestionCommands.NewlineCommand);
            this.AllCommands.Add(SuggestionCommands.NewParagraphCommand);
            this.AllCommands.Add(SuggestionCommands.BoldCommand);
            this.AllCommands.Add(SuggestionCommands.CodeCommand);
            this.AllCommands.Add(SuggestionCommands.ContentLinkCommand);
            this.AllCommands.Add(SuggestionCommands.ExternalLinkCommand);
            this.AllCommands.Add(SuggestionCommands.ListCommand);
            this.AllCommands.Add(SuggestionCommands.ListElementCommand);
            this.AllCommands.Add(SuggestionCommands.TableCommand);
            this.AllCommands.Add(SuggestionCommands.TableRowCommand);
            this.AllCommands.Add(SuggestionCommands.TableCellCommand);
            this.AllCommands.Add(SuggestionCommands.ImageCommand);
        }

        #endregion
        #region Public Properties

        /// <summary>
        /// Gets / sets the value of AllCommands.
        /// </summary>
        public List<RoutedUICommand> AllCommands
        {
            get
            {
                return (List<RoutedUICommand>)GetValue(AllCommandsProperty);
            }
            set
            {
                SetValue(AllCommandsProperty, value);
            }
        }

        #endregion
        #region Public Methods

        /// <summary>
        /// Registers all of the suggestion commands with the entered element.
        /// </summary>
        /// <param name="element"></param>
        public static void RegisterCommandHandlers(UIElement element)
        {
            // First, add the command bindings:
            #region Adding Command Bindings
            
            element.CommandBindings.Add(
                new CommandBinding(SuggestionCommands.NewParagraphCommand,
                    new ExecutedRoutedEventHandler(SuggestionCommands.OnNewParagraphCommand)));

            element.CommandBindings.Add(
                new CommandBinding(SuggestionCommands.NewlineCommand,
                    new ExecutedRoutedEventHandler(SuggestionCommands.OnNewlineCommand)));

            element.CommandBindings.Add(
                new CommandBinding(SuggestionCommands.BoldCommand,
                    new ExecutedRoutedEventHandler(SuggestionCommands.OnBoldCommand)));

            element.CommandBindings.Add(
                new CommandBinding(SuggestionCommands.CodeCommand,
                    new ExecutedRoutedEventHandler(SuggestionCommands.OnCodeCommand)));

            element.CommandBindings.Add(
                new CommandBinding(SuggestionCommands.ExternalLinkCommand,
                    new ExecutedRoutedEventHandler(SuggestionCommands.OnExternalLinkCommand)));

            element.CommandBindings.Add(
                new CommandBinding(SuggestionCommands.ContentLinkCommand,
                    new ExecutedRoutedEventHandler(SuggestionCommands.OnContentLinkCommand)));

            element.CommandBindings.Add(
                new CommandBinding(SuggestionCommands.ListCommand,
                    new ExecutedRoutedEventHandler(SuggestionCommands.OnListCommand)));

            element.CommandBindings.Add(
                new CommandBinding(SuggestionCommands.ListElementCommand,
                    new ExecutedRoutedEventHandler(SuggestionCommands.OnListElementCommand)));

            element.CommandBindings.Add(
                new CommandBinding(SuggestionCommands.TableCommand,
                    new ExecutedRoutedEventHandler(SuggestionCommands.OnTableCommand)));

            element.CommandBindings.Add(
                new CommandBinding(SuggestionCommands.TableRowCommand,
                    new ExecutedRoutedEventHandler(SuggestionCommands.OnTableRowCommand)));

            element.CommandBindings.Add(
                new CommandBinding(SuggestionCommands.TableCellCommand,
                    new ExecutedRoutedEventHandler(SuggestionCommands.OnTableCellCommand)));

            element.CommandBindings.Add(
                new CommandBinding(SuggestionCommands.ImageCommand,
                    new ExecutedRoutedEventHandler(SuggestionCommands.OnImageCommand)));

            #endregion

            // Next, add the input bindings:
            #region Adding Input Bindings

            element.InputBindings.Add(
                new InputBinding(SuggestionCommands.NewParagraphCommand,
                    SuggestionCommands.NewParagraphCommand.InputGestures[0]));

            element.InputBindings.Add(
                new InputBinding(SuggestionCommands.NewlineCommand,
                    SuggestionCommands.NewlineCommand.InputGestures[0]));

            element.InputBindings.Add(
                new InputBinding(SuggestionCommands.BoldCommand,
                    SuggestionCommands.BoldCommand.InputGestures[0]));

            element.InputBindings.Add(
                new InputBinding(SuggestionCommands.CodeCommand,
                    SuggestionCommands.CodeCommand.InputGestures[0]));

            element.InputBindings.Add(
                new InputBinding(SuggestionCommands.ExternalLinkCommand,
                    SuggestionCommands.ExternalLinkCommand.InputGestures[0]));

            element.InputBindings.Add(
                new InputBinding(SuggestionCommands.ContentLinkCommand,
                    SuggestionCommands.ContentLinkCommand.InputGestures[0]));

            element.InputBindings.Add(
                new InputBinding(SuggestionCommands.ListCommand,
                    SuggestionCommands.ListCommand.InputGestures[0]));

            element.InputBindings.Add(
                new InputBinding(SuggestionCommands.ListElementCommand,
                    SuggestionCommands.ListElementCommand.InputGestures[0]));

            element.InputBindings.Add(
                new InputBinding(SuggestionCommands.TableCommand,
                    SuggestionCommands.TableCommand.InputGestures[0]));

            element.InputBindings.Add(
                new InputBinding(SuggestionCommands.TableRowCommand,
                    SuggestionCommands.TableRowCommand.InputGestures[0]));

            element.InputBindings.Add(
                new InputBinding(SuggestionCommands.TableCellCommand,
                    SuggestionCommands.TableCellCommand.InputGestures[0]));

            element.InputBindings.Add(
                new InputBinding(SuggestionCommands.ImageCommand,
                    SuggestionCommands.ImageCommand.InputGestures[0]));

            #endregion

        }

        /// <summary>
        /// This private utility method is used to inser the command text into the entered
        /// text box.  The carret is then moved to the last carret position plus the entered 
        /// offset.
        /// </summary>
        /// <param name="textBox">The text box where the text should be entered</param>
        /// <param name="textToInsert">The text to enter</param>
        /// <param name="carretOffset">The carret offset to add</param>
        private static void InsertTextIntoTextBox(TextBox textBox, string textToInsert, int carretOffset)
        {
            int caretIndex = textBox.CaretIndex;

            // Quick check: see if the character at the caret index is a "<" character!
            // If so, lop the first character off the textToInsert string so that we don't 
            // get two <<'s in a row!
            if(caretIndex > 0)
            {
                if(textBox.Text[caretIndex - 1] == '<')
                {
                    textToInsert = textToInsert.Substring(1);
                    carretOffset--;
                }
            }

            textBox.Text = textBox.Text.Insert(textBox.CaretIndex, textToInsert);
            textBox.CaretIndex = caretIndex + carretOffset;

        }

        #endregion
        #region Event Handlers

        /// <summary>
        /// Called when the new paragraph command is executed.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        public static void OnNewParagraphCommand(object sender, ExecutedRoutedEventArgs args)
        {
            TextBox textBox = sender as TextBox;
            if(textBox == null)
                return;

            StringBuilder xmlBuilder = new StringBuilder();
            xmlBuilder.Append("<NewParagraph />");
            xmlBuilder.Append(Environment.NewLine);
            string xml = xmlBuilder.ToString();

            SuggestionCommands.InsertTextIntoTextBox(textBox, xml, xml.Length);

        }

        /// <summary>
        /// Called when the newline command is invoked.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        public static void OnNewlineCommand(object sender, ExecutedRoutedEventArgs args)
        {
            TextBox textBox = sender as TextBox;
            if(textBox == null)
                return;

            StringBuilder xmlBuilder = new StringBuilder();
            xmlBuilder.Append("<Newline />");
            xmlBuilder.Append(Environment.NewLine);
            string xml = xmlBuilder.ToString();

            SuggestionCommands.InsertTextIntoTextBox(textBox, xml, xml.Length);

        }

        /// <summary>
        /// Called when the bold command is invoked.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        public static void OnBoldCommand(object sender, ExecutedRoutedEventArgs args)
        {
            TextBox textBox = sender as TextBox;
            if(textBox == null)
                return;

            string xml = "<Bold></Bold>";
            SuggestionCommands.InsertTextIntoTextBox(textBox, xml, xml.IndexOf("</"));
        }

        /// <summary>
        /// Called when the bold command is invoked.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        public static void OnCodeCommand(object sender, ExecutedRoutedEventArgs args)
        {
            TextBox textBox = sender as TextBox;
            if(textBox == null)
                return;

            StringBuilder codeBuilder = new StringBuilder();
            codeBuilder.Append("<Code><![CDATA[");
            codeBuilder.Append(Environment.NewLine);
            codeBuilder.Append(Environment.NewLine);
            codeBuilder.Append("]]></Code>");

            string xml = codeBuilder.ToString();
            SuggestionCommands.InsertTextIntoTextBox(textBox, xml, xml.LastIndexOf(Environment.NewLine));

        }

        /// <summary>
        /// Called when the external link command is invoked.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        public static void OnExternalLinkCommand(object sender, ExecutedRoutedEventArgs args)
        {
            TextBox textBox = sender as TextBox;
            if(textBox == null)
                return;

            string xml = "<ExternalLink href=\"\"></ExternalLink>";
            SuggestionCommands.InsertTextIntoTextBox(textBox, xml, xml.LastIndexOf("\""));
        }

        /// <summary>
        /// Called when the content link command is invoked.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        public static void OnContentLinkCommand(object sender, ExecutedRoutedEventArgs args)
        {
            TextBox textBox = sender as TextBox;
            if(textBox == null)
                return;

            string xml = "<ContentLink href=\"\"></ContentLink>";
            SuggestionCommands.InsertTextIntoTextBox(textBox, xml, xml.LastIndexOf("</"));
        }

        /// <summary>
        /// Called when the ListCommand is invoked.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        public static void OnListCommand(object sender, ExecutedRoutedEventArgs args)
        {
            TextBox textBox = sender as TextBox;
            if(textBox == null)
                return;

            StringBuilder xmlBuilder = new StringBuilder();
            xmlBuilder.Append("<List>");
            xmlBuilder.Append(Environment.NewLine);
            xmlBuilder.Append("<ListElement></ListElement>");
            xmlBuilder.Append(Environment.NewLine);
            xmlBuilder.Append("</List>");

            string xml = xmlBuilder.ToString();
            SuggestionCommands.InsertTextIntoTextBox(textBox, xml, xml.IndexOf("</ListElement>"));

        }

        /// <summary>
        /// Called when the list element command is invoked.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        public static void OnListElementCommand(object sender, ExecutedRoutedEventArgs args)
        {
            TextBox textBox = sender as TextBox;
            if(textBox == null)
                return;

            string xml = "<ListElement></ListElement>";
            SuggestionCommands.InsertTextIntoTextBox(textBox, xml, xml.IndexOf("</"));

        }

        /// <summary>
        /// Called when the table command is invoked.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        public static void OnTableCommand(object sender, ExecutedRoutedEventArgs args)
        {
            TextBox textBox = sender as TextBox;
            if(textBox == null)
                return;

            StringBuilder xmlBuilder = new StringBuilder();
            xmlBuilder.Append("<Table>");
            xmlBuilder.Append(Environment.NewLine);
            xmlBuilder.Append("<TableRow>");
            xmlBuilder.Append(Environment.NewLine);
            xmlBuilder.Append("<TableCell WidthPercent=\"\"></TableCell>");
            xmlBuilder.Append(Environment.NewLine);
            xmlBuilder.Append("</TableRow>");
            xmlBuilder.Append(Environment.NewLine);
            xmlBuilder.Append("</Table>");

            string xml = xmlBuilder.ToString();
            SuggestionCommands.InsertTextIntoTextBox(textBox, xml, xml.LastIndexOf("\""));

        }

        /// <summary>
        /// Called when the TableRow command is invoked.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        public static void OnTableRowCommand(object sender, ExecutedRoutedEventArgs args)
        {
            TextBox textBox = sender as TextBox;
            if(textBox == null)
                return;

            StringBuilder xmlBuilder = new StringBuilder();
            xmlBuilder.Append("<TableRow>");
            xmlBuilder.Append(Environment.NewLine);
            xmlBuilder.Append("<TableCell WidthPercent=\"\"></TableCell>");
            xmlBuilder.Append(Environment.NewLine);
            xmlBuilder.Append("</TableRow>");

            string xml = xmlBuilder.ToString();
            SuggestionCommands.InsertTextIntoTextBox(textBox, xml, xml.LastIndexOf("\""));

        }

        /// <summary>
        /// Called when the table cell command is invoked.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        public static void OnTableCellCommand(object sender, ExecutedRoutedEventArgs args)
        {
            TextBox textBox = sender as TextBox;
            if(textBox == null)
                return;

            string xml = "<TableCell WidthPercent=\"\"></TableCell>";
            SuggestionCommands.InsertTextIntoTextBox(textBox, xml, xml.LastIndexOf("\""));

        }

        /// <summary>
        /// Called when the Image command is invoked.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        public static void OnImageCommand(object sender, ExecutedRoutedEventArgs args)
        {
            TextBox textBox = sender as TextBox;
            if (textBox == null)
                return;

            string xml = "<Image href=\"\" width=\"\" height=\"\" />";
            SuggestionCommands.InsertTextIntoTextBox(textBox, xml, xml.IndexOf("\""));

        }

        #endregion

    }

}
